a) Obtener todos los componentes pertenecientes al Ć­ndice S&P 500, en cada momento del tiempo, desde el 2010 hasta el primer trimestre del 2022.

Para obtener todos los activos pertenecientes al ƭndice S&P 500, en cada momento del tiempo, debemos hacer un web scraping. Tomaremos como referencia la pƔgina de Wikipedia.

library(rvest)
# Web-scrape SP500 stock list
sp_500 <- read_html("https://en.wikipedia.org/wiki/List_of_S%26P_500_companies") %>%
  html_node("table.wikitable") %>%
  html_table() 
# Format names
names(sp_500) <- sp_500 %>% 
  names() %>% 
  str_to_lower() %>% 
  make.names()
# Show results
sp_500 

A los efectos del trabajo, nos interesa conocer los cambios que hubo en el índice. Para ello, generaremos un código que nos permita conocer qué cambios hubo en los componentes pertencientes al índice. Nos dirÔ qué activos fueron reemplazados por qué otros y en qué momento.

wikispx <- read_html('https://en.wikipedia.org/wiki/List_of_S%26P_500_companies')
currentconstituents <- wikispx %>%
  html_node('#constituents') %>%
  html_table(header = TRUE)
currentconstituents
currentconstituents$Date <- currentconstituents$`Date first added`

spxchanges <- wikispx %>%
  html_node('#changes') %>%
  html_table(header = FALSE, fill = TRUE) %>%
  dplyr::filter(row_number() > 2) %>% # First two rows are headers
  `colnames<-`(c('Date','AddTicker','AddName','RemovedTicker','RemovedName','Reason')) %>%
  mutate(Date = as.Date(Date, format = '%B %d, %Y'),
         year = year(Date),
         month = month(Date))
spxchanges

b) Conformar un portafolio de 20 acciones, donde haya por lo menos 1 acción de cada sector (según la definición de la clasificación GICS). Dichas acciones podrÔn volver a elegirse al final de cada trimestre (si es que quieren cambiar el set de activos a considerar). Como punto de partida las acciones elegidas deberían ser parte del S&P 500 al momento de la elección.

Antes de conformar el portafolio, es interesante deternos a analizar los distinos sectores y la frecuencia con la que aparecen:

sp_500 %>%
  # Summarise data by frequency
  group_by(gics.sector) %>%
  summarise(count = n()) %>%
  # Visualize 
  ggplot(aes(x = gics.sector %>% fct_reorder(count),
             y = count
  )) + 
  geom_bar(stat = "identity") +
  geom_text(aes(label = count), size = 3, nudge_y = 4, nudge_x = .1) + 
  scale_y_continuous(limits = c(0,100)) +
  ggtitle(label = "Sector Frequency Among SP500 Stocks") +
  xlab(label = "GICS Sector") +
  theme(plot.title = element_text(size = 16)) + 
  coord_flip() 

Ahora, una vez que entendimos que hay once senctores, debemos pasar a elegir los activos con los que trabajaremos en el resto del ejercicio. Para ello, haremos una selección aleatoria en dos partes: por un lado, tomaremos una acción de cada sector (cumpliendo el requerimiento) y, las nueve acciones restantes serÔn elegidas de manera aleatoria. Desde ya, tendremos que asegurarnos que ninguna acción se repita.

#Selecciono a partir del sector
sample_part_1 <- sp_500 %>%
  group_by(gics.sector)%>%
  slice_sample(n=1) 
#Selección aleatoria
sample_part_2 <- sample(sp_500$symbol, 9)
#Selección completa
full_sample <- data.table(symbol= c(sample_part_1$symbol, sample_part_2))

#Chequeo que no se repitan
if(length(unique(full_sample$symbol)) == 20){
  "TRUE"
}
## [1] "TRUE"

Una vez realizada la selección de las acciones del portafolio, almacenaremos los nombres de los sĆ­mbolos en ā€˜full_sample.’

#
full_sample <- data.table(symbols= c('GOOGL', 'NKE', 'CAG', 'COP', 'SPGI', 'TMO',  'CMI', 
                                     'GLW', 'HWM', 'ESS', 'NRG',  'NDSN',  'MNST', 'VICI', 
                                     'ADP', 'KIM', 'ADBE',  'LIN',  'FITB', 'HPE'))

c) Obtener los precios de dichas acciones (extendiƩndose hasta finales del segundo trimestre de 2022 -1Q22- para dichas series de precios).

Con la función tq_get obtendremos los precios de las acciones desde 2010 hasta junio 2022.

prices_sp_500 <- full_sample$symbol %>%
  tq_get(get  = "stock.prices",
         from = "2010-01-01",
         to   = "2022-06-30") %>%
  group_by(symbol) 

d) Obtener los precios de por lo menos 3 fondos de renta variable (ETFs/Mutual Funds de manejo pasivo o activo) que tengan como benchmark al S&P 500.

De la misma manera que con el ítem anterior, obtendremos los precios de los ETF con la función tq_get.

QUE SON LOS ETF?????

prices_etf <- c("XLK", "IVV", "VOO")  %>%
  tq_get(get  = "stock.prices",
         from = "2010-01-01",
         to   = "2022-03-31") %>%
  group_by(symbol) 

e) Realizar una optimización de portafolios, para cada uno de los trimestres -en base a datos históricos-, encontrando los siguientes:

  • Portafolio de MĆ­nima Varianza Global.
  • Portafolio de MĆ”ximo Ratio de Sharpe.

Para poder realizar la optimización del portafolio, primero debemos obtener los retornos de las acciones.

# Creamos un vector con los sĆ­mbolos
symbols <- full_sample$symbol
# Cargamos la data de los precios de 2010 hasta junio 2022
prices <- quantmod::getSymbols(
  Symbols = symbols,
  src = "yahoo",
  from = "2010-1-1",
  to = "2022-6-30",
  auto.assign = TRUE,
  warnings = FALSE
) %>%
  purrr::map(.f = ~ quantmod::Ad(get(x = .x))) %>%
  purrr::reduce(.f = merge) %>%
  `colnames<-`(value = symbols)

Luego, obtenemos los retornos mensuales:

asset_returns_xts <- xts::to.monthly(
  x = prices,
  drop.time = TRUE,
  indexAt = "lastof",
  OHLC = FALSE
) %>%
  PerformanceAnalytics::Return.calculate(method = "discrete") %>%
  stats::na.omit()

Lo mostramos en la siguiente tabla:

asset_returns_xts_3[,1:10]%>%DT::datatable(extensions = 'Buttons', 
                       options = list(dom = 'Blfrtip', 
                                      buttons = c('copy', 'csv', 'excel', 'pdf', 'print'), 
                                      columnDefs = list(list(className = 'dt-center', targets = 5)),
                                      pageLength = 5, autoWidth = TRUE ))
asset_returns_xts_3[,11:20]%>%DT::datatable(extensions = 'Buttons', 
                       options = list(dom = 'Blfrtip', 
                                      buttons = c('copy', 'csv', 'excel', 'pdf', 'print'), 
                                      columnDefs = list(list(className = 'dt-center', targets = 5)),
                                      pageLength = 5, autoWidth = TRUE ))

Adicionalmente, podemos armar un grƔfico con los retornos de, por ejemplo, diez de nuestras acciones del portafolio:

Ahora, definimos los trimestres a analizar.

trimesters <- seq(as.Date("2010-01-01"), as.Date("2022-06-30"), by=90)
trimesters
##  [1] "2010-01-01" "2010-04-01" "2010-06-30" "2010-09-28" "2010-12-27"
##  [6] "2011-03-27" "2011-06-25" "2011-09-23" "2011-12-22" "2012-03-21"
## [11] "2012-06-19" "2012-09-17" "2012-12-16" "2013-03-16" "2013-06-14"
## [16] "2013-09-12" "2013-12-11" "2014-03-11" "2014-06-09" "2014-09-07"
## [21] "2014-12-06" "2015-03-06" "2015-06-04" "2015-09-02" "2015-12-01"
## [26] "2016-02-29" "2016-05-29" "2016-08-27" "2016-11-25" "2017-02-23"
## [31] "2017-05-24" "2017-08-22" "2017-11-20" "2018-02-18" "2018-05-19"
## [36] "2018-08-17" "2018-11-15" "2019-02-13" "2019-05-14" "2019-08-12"
## [41] "2019-11-10" "2020-02-08" "2020-05-08" "2020-08-06" "2020-11-04"
## [46] "2021-02-02" "2021-05-03" "2021-08-01" "2021-10-30" "2022-01-28"
## [51] "2022-04-28"

Optimización general:

data_p2 = asset_returns_xts
# create specification
port = portfolio.spec(assets = c(colnames(data_p2)))
# add long only constraint
port = add.constraint(portfolio = port, type = "long_only")
# add full investment contraint
port = add.constraint(portfolio = port, type = "full_investment")

# objective: manimise risk
port_rnd = add.objective(portfolio = port, type = "risk", name = "StdDev")

# objective: maximise return
port_rnd = add.objective(portfolio = port_rnd, type = "return", name = "mean")

# 1. optimise random portfolios

rand_p = optimize.portfolio(R = data_p2, portfolio = port_rnd, optimize_method = "random",
                            trace = TRUE, search_size = 100)

port_msd = add.objective(portfolio = port, type = "risk", name = "StdDev")
minvar1 = optimize.portfolio(R = data_p2, portfolio = port_msd, optimize_method = "ROI")
minvar1
## ***********************************
## PortfolioAnalytics Optimization
## ***********************************
## 
## Call:
## optimize.portfolio(R = data_p2, portfolio = port_msd, optimize_method = "ROI")
## 
## Optimal Weights:
##  GOOGL    NKE    CAG    COP   SPGI    TMO    CMI    GLW    HWM    ESS    NRG 
## 0.0000 0.0439 0.1448 0.0000 0.0000 0.1769 0.0000 0.0785 0.0000 0.2240 0.0119 
##   NDSN   MNST   VICI    ADP    KIM   ADBE    LIN   FITB    HPE 
## 0.0342 0.0464 0.0000 0.1229 0.0000 0.0595 0.0569 0.0000 0.0000 
## 
## Objective Measure:
##  StdDev 
## 0.04617

Graficamos:

tailoredFrontierPlot(eff_front2, sharpeRatio = FALSE, risk = "Sigma")